package com.sl.biz.modular.datasource.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.druid.util.JdbcUtils;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.google.common.collect.Maps;
import com.sl.biz.modular.datasource.controller.request.*;
import com.sl.biz.modular.datasource.dao.domain.dto.ProcessDataSourceDTO;
import com.sl.biz.modular.datasource.dao.domain.entity.ProcessDataSourceDO;
import com.sl.biz.modular.datasource.dao.service.ProcessDatasourceDaoService;
import com.sl.biz.modular.datasource.service.DatasourceCustomerService;
import com.sl.biz.modular.datasource.service.ProcessDatasourceBizService;
import com.sl.common.enums.CommonDsiableFlagEnum;
import com.sl.core.engine.datasource.DataSourceManager;
import com.sl.core.engine.datasource.DataSourceQueryService;
import com.sl.core.engine.datasource.domain.dto.*;
import com.sl.core.engine.meta.row.ProcessRowDescriptor;
import lombok.RequiredArgsConstructor;
import org.apache.commons.compress.utils.Lists;
import org.apache.hop.core.exception.HopPluginException;
import org.apache.hop.core.row.value.ValueMetaFactory;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
public class ProcessDatasourceBizServiceImpl implements ProcessDatasourceBizService, DataSourceQueryService {

    private final ProcessDatasourceDaoService datasourceDaoService;

    private final DynamicDataSourceProperties dynamicDataSourceProperties;

    private final List<DatasourceCustomerService> datasourceCustomerServices;

    @Override
    public List<ProcessDataSourceDTO> list() {
        List<ProcessDataSourceDTO> processDataSourceDTOS = datasourceDaoService.queryByList();
        if (CollUtil.isEmpty(processDataSourceDTOS)) {
            processDataSourceDTOS = Lists.newArrayList();
        }

        ProcessDataSourceDTO processDataSourceDTO = buildDefault();
        processDataSourceDTOS.add(processDataSourceDTO);
        //这里还要把本地链接搞进去
        return processDataSourceDTOS;
    }

    private ProcessDataSourceDTO buildDefault() {
        ProcessDataSourceDTO processDataSourceDTO = new ProcessDataSourceDTO();
        processDataSourceDTO.setDatasourceCode("default");
        processDataSourceDTO.setDatasourceName("系统数据源");

        Map<String, DataSourceProperty> datasource = dynamicDataSourceProperties.getDatasource();
        DataSourceProperty dataSourceProperty = datasource.get(dynamicDataSourceProperties.getPrimary());
        DataSourceProperties dataSourceProperties = new DataSourceProperties();
//            dataSource.setGenerateUniqueName(false);
        dataSourceProperties.setName(dataSourceProperty.getPoolName());
        dataSourceProperties.setType(dataSourceProperty.getType());
        dataSourceProperties.setDriverClassName(dataSourceProperty.getDriverClassName());
        dataSourceProperties.setUrl(dataSourceProperty.getUrl());
        dataSourceProperties.setUsername(dataSourceProperty.getUsername());
        dataSourceProperties.setPassword(dataSourceProperty.getPassword());


        processDataSourceDTO.setDriverClassName(dataSourceProperties.getDriverClassName());
        processDataSourceDTO.setUsername(dataSourceProperties.getUsername());

        processDataSourceDTO.setPassword(dataSourceProperties.getPassword());
        String url = dataSourceProperties.getUrl();
        URI uri = URLUtil.toURI(url.replace("jdbc:", ""), false);

        processDataSourceDTO.setIp(uri.getHost());
        processDataSourceDTO.setPort(uri.getPort() + "");
        processDataSourceDTO.setUrl(url);
        processDataSourceDTO.setDb(uri.getPath().replace("/", ""));
        processDataSourceDTO.setDbTypeCode(JdbcUtils.getDbType(dataSourceProperties.getUrl(), dataSourceProperties.getDriverClassName()));
        processDataSourceDTO.setDisableFlag(0);
        processDataSourceDTO.setId(processDataSourceDTO.getDatasourceCode());
        processDataSourceDTO.setCreateTime(new Date());
        processDataSourceDTO.setModifyTime(new Date());
        processDataSourceDTO.setDeleted(0);
        return processDataSourceDTO;
    }


    @Override
    public Boolean save(ProcessDataSourceDO addRequest) {
        String id = addRequest.getId();
        if (StrUtil.isBlank(id)) {
            addRequest.setDisableFlag(CommonDsiableFlagEnum.DISABLE_FLAG.getCode());
            datasourceDaoService.save(addRequest);
        } else {
            datasourceDaoService.updateById(addRequest);
        }
        DataSourceManager.INSTANCE.clear();
        return Boolean.TRUE;
    }

    @Override
    public ProcessDataSourceDTO getById(ProcessDataSourceDTO addRequest) {
        ProcessDataSourceDO byId = getById(addRequest.getId(), addRequest.getType());
        if (ObjectUtil.isNull(byId)) {
            return null;
        }
        ProcessDataSourceDTO processDataSourceDTO = BeanUtil.copyProperties(byId, ProcessDataSourceDTO.class);

        processDataSourceDTO.setType(addRequest.getType());
        return processDataSourceDTO;
    }

    private DatasourceCustomerService findTypeService(String type) {
        return datasourceCustomerServices.stream().filter(v -> {
            return StrUtil.equals(v.type(), type);
        }).findFirst().orElse(null);
    }

    @Override
    public Boolean removeById(ProcessDataSourceDO removeByIdRequest) {
        datasourceDaoService.removeById(removeByIdRequest.getId());
        DataSourceManager.INSTANCE.clear();
        return Boolean.TRUE;
    }

    @Override
    public Boolean setStatusFlag(ProcessDataSourceDO assertUserUse) {

        Boolean b = datasourceDaoService.setStatusFlag(assertUserUse);
        DataSourceManager.INSTANCE.clear();
        return b;
    }

    @Override
    public String testConnection(ProcessDataSourceDO assertUserUse) {
        String id = assertUserUse.getId();
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(assertUserUse, DataSourceDTO.class);

        if (StrUtil.equals(id, "default")) {
            Properties properties = new Properties();
            dataSourceDTO.setProperties(properties);
        }
        String s = DataSourceManager.INSTANCE.testConnection(dataSourceDTO);
        return s;
    }

    @Override
    public List<String> querySchemaList(ProcessDataSourceDTO assertUserUse) {
        String id = assertUserUse.getId();
        String type = assertUserUse.getType();

        Properties properties = new Properties();
        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return Collections.emptyList();
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return Arrays.stream(DataSourceManager.INSTANCE.querySchemaList(dataSourceDTO)).toList();
    }

    private ProcessDataSourceDO getById(String id, String type) {
        ProcessDataSourceDO byId = null;
        Properties properties = new Properties();

        if (StrUtil.equals(id, "default")) {
            byId = BeanUtil.copyProperties(buildDefault(), ProcessDataSourceDO.class);
//            properties.put(BaseDatabaseMeta.ATTRIBUTE_USE_POOLING, "Y");
        } else if (StrUtil.isNotBlank(type)) {
            DatasourceCustomerService typeService = findTypeService(type);
            if (ObjectUtil.isNotNull(typeService)) {
                byId = typeService.getById(id);
            }
        } else {
            byId = datasourceDaoService.getById(id);
        }
        return byId;
    }

    @Override
    public List<String> queryCatalogList(ProcessDataSourceDTO assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return Collections.emptyList();
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return Arrays.stream(DataSourceManager.INSTANCE.queryCatalogList(dataSourceDTO)).toList();
    }

    @Override
    public Boolean supportsCatalogs(ProcessDataSourceDTO assertUserUse) {
        String id = assertUserUse.getId();
        String type = assertUserUse.getType();

        Properties properties = new Properties();
        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return false;
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.supportsCatalogs(dataSourceDTO);
    }

    @Override
    public Boolean supportsSchemas(ProcessDataSourceDTO assertUserUse) {
        String id = assertUserUse.getId();
        String type = assertUserUse.getType();

        Properties properties = new Properties();
        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return false;
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.supportsSchemas(dataSourceDTO);
    }

    @Override
    public List<String> queryTableList(ProcessDataSourceQueryTableListRequest assertUserUse) {
        String id = assertUserUse.getId();
        String type = assertUserUse.getType();

        Properties properties = new Properties();
        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return Collections.emptyList();
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryTableList(dataSourceDTO, assertUserUse.getCatalogName());
    }

    @Override
    public List<TableColumnDTO> queryTableFieldList(ProcessDataSourceQueryTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);
        if(ObjectUtil.isNull(byId)){
            return Collections.emptyList();
        }
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        ProcessRowDescriptor rowDescriptor = DataSourceManager.INSTANCE.queryTableFieldList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());
        List<TableColumnDTO> collect = rowDescriptor.getValueMetaList().stream().map(v -> {
            TableColumnDTO tableColumnDTO = new TableColumnDTO();
            tableColumnDTO.setOriginalColumnTypeName(v.getOriginalColumnTypeName());

            try {
                tableColumnDTO.setColumnType(ValueMetaFactory.createValueMeta(v.getType()).toStringMeta());
            } catch (HopPluginException e) {
                throw new RuntimeException(e);
            }

            tableColumnDTO.setColumnLength(v.getOriginalPrecision() + "");
            tableColumnDTO.setColumnName(v.getName());
            return tableColumnDTO;
        }).collect(Collectors.toList());

        List<TableKeysDTO> tableKeysDTOS = DataSourceManager.INSTANCE.queryKeysList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());
        if (ObjectUtil.isNull(tableKeysDTOS)) {
            return collect;
        }
        for (TableKeysDTO tableKeysDTO : tableKeysDTOS) {
            String columnName = tableKeysDTO.getColumnName();

            Boolean isPrimary = tableKeysDTO.getIsPrimary();
            if (BooleanUtil.isTrue(isPrimary)) {
                List<TableColumnDTO> tableColumnDTOList = collect.stream().filter(v -> v.getColumnName().equals(columnName)).collect(Collectors.toList());
                for (TableColumnDTO tableColumnDTO : tableColumnDTOList) {
                    tableColumnDTO.setIsPrimary(isPrimary);
                }
            }
        }

        return collect;
    }

    @Override
    public Map<String, Object> queryTableFieldModelList(ProcessDataSourceQueryTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        ProcessRowDescriptor rowDescriptor = DataSourceManager.INSTANCE.queryTableFieldList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());

        Map<String, Object> inputDefine = rowDescriptor.toFieldDefine();
        return inputDefine;

    }

    @Override
    public List<ProcessDataSourceDTO> getByIds(List<String> datasourceIds) {
        List<ProcessDataSourceDTO> processDataSourceDTOS = new ArrayList<>();
        for (String datasourceId : datasourceIds) {
            ProcessDataSourceDO byId = getById(datasourceId, null);
            ProcessDataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, ProcessDataSourceDTO.class);
            processDataSourceDTOS.add(dataSourceDTO);
        }
        return processDataSourceDTOS;
    }


    @Override
    public String queryTableDdl(ProcessDataSourceQueryTableDdlRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryTableDdl(dataSourceDTO, assertUserUse.getTableName());
    }

    @Override
    public SqlQueryResultDTO run(ProcessDataSourceRunRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();
        if (StrUtil.isNotBlank(type)) {
            properties.putIfAbsent("type", type);
        }

        ProcessDataSourceDO byId = getById(id, type);

        Object args = assertUserUse.getArgs();
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        String sql = assertUserUse.getSql();

        List<String> split = StrUtil.split(sql, ";");

        Map<String, Object> map = Maps.newHashMap();
        map.put("$", args);
        return DataSourceManager.INSTANCE.run(dataSourceDTO, CollUtil.get(split,0), map);

    }

    @Override
    public List<TableIndexDTO> queryTableIndexList(ProcessDataSourceQueryTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryTableIndexList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());
    }

    @Override
    public List<TableKeysDTO> queryKeysList(ProcessDataSourceQueryTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();
        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryKeysList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());
    }

    @Override
    public Boolean addUpdateTable(ProcessDataSourceAddUpdateTableRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.addUpdateTable(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(),
                assertUserUse.getSourceTableName());
    }

    @Override
    public Boolean removeTable(ProcessDataSourceAddUpdateTableRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);
        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.removeTable(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName());
    }

    @Override
    public Boolean addUpdateIndex(ProcessDataSourceAddUpdateIndexRequest assertUserUse) {
        String id = assertUserUse.getId();
        String type = assertUserUse.getType();

        Properties properties = new Properties();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.addUpdateIndex(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(), assertUserUse.getIndexName(),
                assertUserUse.getSourceIndexName(),
                assertUserUse.getFieldNameList());
    }

    @Override
    public String queryCreateTableFieldList(ProcessDataSourceQueryCreateTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryCreateTableFieldList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(),
                assertUserUse.getColumnList());
    }

    @Override
    public Boolean executeDdl(ProcessDataSourceExecuteDdlRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();
        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.executeDdl(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(),
                assertUserUse.getDdl());
    }

    @Override
    public String queryUpdateTableFieldList(ProcessDataSourceQueryUpdateTableMetaListRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();
        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.queryUpdateTableFieldList(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(),
                assertUserUse.getColumnList());
    }

    @Override
    public Boolean dropIndex(ProcessDataSourceDropIndexRequest assertUserUse) {
        String id = assertUserUse.getId();
        Properties properties = new Properties();
        String type = assertUserUse.getType();

        ProcessDataSourceDO byId = getById(id, type);

        DataSourceDTO dataSourceDTO = BeanUtil.copyProperties(byId, DataSourceDTO.class);
        dataSourceDTO.setProperties(properties);
        return DataSourceManager.INSTANCE.dropIndex(dataSourceDTO, assertUserUse.getCatalogName(), assertUserUse.getTableName(),
                assertUserUse.getIndexName());
    }


    @Override
    public List<DataSourceDTO> queryList() {
        return BeanUtil.copyToList(this.list(), DataSourceDTO.class);
    }

    @Override
    public DataSourceDTO queryById(String datasourceId, Properties properties) {
        ProcessDataSourceDTO processDataSourceDO = new ProcessDataSourceDTO();
        processDataSourceDO.setId(datasourceId);
        if (ObjectUtil.isNotNull(properties)) {
            processDataSourceDO.setType(properties.getProperty("type"));
        }
        return BeanUtil.copyProperties(getById(processDataSourceDO), DataSourceDTO.class);
    }
}
